<?php

namespace App\Kuafu\CouponExt\Channel\MeiTuan;

use App\Kuafu\Base\Util\EnvUtil;
use App\Kuafu\CouponExt\Channel\CouponExtBase;
use App\Kuafu\CouponExt\Channel\CouponExtInterface;
use App\Kuafu\CouponExt\Enum\CouponExtCodeEnum;
use App\Kuafu\CouponExt\Enum\CouponExtEnum;
use App\Kuafu\CouponExt\Exceptions\CouponExtException;
use App\Kuafu\Pay\Service\ShopPayService;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Shop\Service\ShopService;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Service\WaiMaiShopService;
use App\Library\FeiShu\SendFs;
use App\Library\SnowFlake\SnowFlake;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Facades\Log;
use App\Kuafu\Base\Service\BaseService;
use Kuafu\GeneralFs\BuildFs;
use KuafuComposer\ApiTime\Service\ApiTimeService;

class MeiTuanBase extends CouponExtBase implements CouponExtInterface
{
    // 文档地址：https://developer.meituan.com/docs/api/tuangou-coupon-queryTradeDetail
    private $developerId;
    private $signKey;
    private $appAuthToken;
    private $eId;
    private $eName;

    private      $host;
    private      $shopId;
    private      $fsWarning;
    private Shop $shop;

    const TIME_OUT     = 10;
    const DEFAULT_HOST = 'https://api-open-cater.meituan.com';

    public const TEST_COUPON_CODE = [
        '111122223331',
        '111122223332',
        '111122223333',
        '111122223334',
        '111122223335',
        '111122223336',
        '111122223337',
        '111122223338',
        '111122223339',
    ]; // 开发用的假券

    const BUSINESS_ID_1 = 1; // 团购

    /**
     * @param int|Shop $shop
     * @param bool $configLoad
     * @param bool $fsWarning
     * @throws CouponExtException
     */
    public function __construct($shop, bool $configLoad = true, bool $fsWarning = true)
    {
        if ($shop instanceof Shop) {
            $this->shop   = $shop;
            $this->shopId = $shop->id;
        } else {
            $this->shopId = $shop;
            $this->shop   = ShopService::getShopInfo($this->shopId);
        }

        $this->fsWarning = $fsWarning;

        parent::__construct();
        $this->loadDeveloperConfig()->loadHost(self::DEFAULT_HOST);

        if ($configLoad) {
            $this->loadConfig();
        }
    }

    /**
     * 开发者配置
     * @return $this
     */
    private function loadDeveloperConfig()
    {
        $this->developerId = 106643;
        $this->signKey     = 'myssey0bdsl2katc';

        return $this;
    }

    private function loadConfig()
    {
        $shopId = $this->shopId;

        if ($this->shop) {
            $config = $this->shop->waimaiShopMappings ? $this->shop->waimaiShopMappings->where('sourceType',
                WaiMaiDBEnum::SOURCE_TYPE_MT_TG)->first() : null;
        } else {
            // 通过shopId获取开发者信息
            $config = WaiMaiShopService::getMappingCache($shopId, WaiMaiDBEnum::SOURCE_TYPE_MT_TG);
        }

        if (!$config && $this->fsWarning) {

            $shop     = ShopService::getShopInfo($shopId);
            $shopName = $shop->name ?? '';
            $shopCode = $shop->shopCode ?? '';

            BuildFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)
                   ->setTitleContent('美团DB店铺配置为空', "shopId:{$shopId} {$shopCode} {$shopName}")
                   ->send();

            throw new CouponExtException(CouponExtCodeEnum::ERR_MEITUAN_CONFIG_EMPTY);
        }

        $appAuthToken = $config['appAuthToken'] ?? '';
        $eId          = $config['thirdShopId'] ?? '';
        $eName        = $config['thirdShopName'] ?? '';
        if (!$appAuthToken || !$eId || !$eName) {
            BuildFs::newRobot(SendFs::ROBOT_BUSINESS_WARING)
                   ->setTitleContent('美团团购DB店铺配置错误',
                       "shopId:{$shopId} appAuthToken:{$appAuthToken} eId:{$eId} eName:{$eName}")->send();
            throw new CouponExtException(CouponExtCodeEnum::ERR_MEITUAN_CONFIG_EMPTY);
        }

        $this->appAuthToken = $appAuthToken;
        $this->eId          = $eId;
        $this->eName        = $eName;

        return $this;
    }

    /**
     * 加载Host
     * @param string $host
     * @return $this
     */
    public function loadHost(string $host)
    {
        $this->host = $host;
        return $this;
    }

    /**
     * 数字签名
     * @param array $params
     * @return string
     */
    private function signature(array $params): string
    {
        $result = $this->signKey;

        ksort($params);

        foreach ($params as $key => $param) {
            $param  = is_array($param) ? json_encode($param) : $param;
            $result .= $key . $param;
        }

        return strtolower(sha1($result));
    }

    /**
     * 接口通用请求参数
     * @return array
     */
    private function commonParam(): array
    {
        return [
            'appAuthToken' => $this->appAuthToken,
            'charset'      => 'UTF-8',
            'timestamp'    => time(),
            'version'      => 2,
            'developerId'  => $this->developerId,
            'businessId'   => 1, // 1团购 2外卖
        ];
    }

    /**
     * post请求
     * @param string $path
     * @param array $bizParam 业务参数
     * @return array
     * @throws GuzzleException
     */
    private function curlPost(string $path, array $bizParam): array
    {
        // 业务参数+公共参数
        $params = ['biz' => json_encode($bizParam)] + $this->commonParam();

        // +验签
        $params['sign'] = $this->signature($params);

        $startMicroTime = microtime(true);

        $response = (new Client([
            'timeout' => self::TIME_OUT,
            'headers' => [
                'Content-Type' => "application/x-www-form-urlencoded",
            ],
        ]))->post($this->host . $path, [RequestOptions::FORM_PARAMS => $params]);

        try {
            (new ApiTimeService('mtTg'))->add($path, $startMicroTime);
        } catch (\Exception $e) {
            Log::warning($e);
        }

        $uuid = md5(microtime(true) . rand(1, 10000));

        Log::info('MeiTuan Request:', ['uuid' => $uuid, 'url' => $this->host . $path, ['form_params' => $params]]);
        $s = json_decode($response->getBody()->getContents(), true) ?? [];
        Log::info('MeiTuan Response:', ['uuid' => $uuid, 'data' => $s]);

        return $s;
    }

    // 店铺映射
    public function storeMap(int $businessId = self::BUSINESS_ID_1)
    {
        if ($this->shop) {
            $shopName = $this->shop->name ?? '';
        } else {
            $shopName = ShopService::getShopInfo($this->shopId)->name ?? '';
        }


        $params         = [
            'developerId' => $this->developerId,
            'businessId'  => $businessId, // 1团购 2外卖
            'ePoiId'      => self::encodeMTShopId($this->shopId), // ERP厂商针对该门店自行定义的id
            'ePoiName'    => $shopName, // ERP商家门店名
            'timestamp'   => time() * 1000,
        ];
        $params['sign'] = $this->signature($params);

        return 'https://open-erp.meituan.com/storemap?' . http_build_query($params);
    }

    // 店铺取消映射
    public function unstoreMap(int $businessId = self::BUSINESS_ID_1)
    {
        $params = [
            'appAuthToken' => $this->appAuthToken,
            'businessId'   => $businessId,
            'timestamp'    => time() * 1000,
        ];

        $params['sign'] = $this->signature($params);

        return 'https://open-erp.meituan.com/releasebinding?' . http_build_query($params);
    }

    /**
     * 门店套餐映射
     * @return array
     * @throws GuzzleException
     */
    public function menuQuery(): array
    {
        /*
         * -1 全部状态（含以下所有）
            0 结束售卖
            1 正在售卖
            2 隐藏单，前台通过POI列表及deal列表都不可见，但是通过收藏或url可以直接访问并购买
            4 未开始售卖
            5 不可购买
         * */

        $params = [
            'dealStatus' => -1,
        ];

        return $this->curlPost('/tuangou/coupon/querySetMealList', $params);
    }

    /**
     * 验券准备
     * @param string $couponCode
     * @return array
     * @throws GuzzleException
     */
    public function couponPrepare(string $couponCode): array
    {
        // ****** 自己调试用 start ******
        if (in_array($couponCode, self::TEST_COUPON_CODE) && config('app.env') != 'prod') {
            $format         = self::returnFormatData(true, '', '', []);
            $format['data'] = CouponExtBase::formatPrepareResponseData(
                5,
                1,
                1,
                '自己测试的牛逼券',
                $couponCode,
                CouponExtEnum::COUPON_TYPE_MONEY,
                3400,
                4000,
                0,
                strtotime(20331231),
                CouponExtEnum::EXT_PLATFORM_MEITUAN
            );
            return $format;
        }
        // ****** 自己调试用 end ******

        $params = [
            'code' => $couponCode,
        ];

        $response = $this->curlPost('/tuangou/ng/coupon/msprepare', $params);
        $format   = self::commonReturn($response);
        if (!$format['status']) {
            return $format;
        }
        $oriData = $format['oriData'];

        $format['data'] = CouponExtBase::formatPrepareResponseData(
            $oriData['count'],
            $oriData['minConsume'],
            $oriData['dealId'],
            $oriData['dealTitle'],
            $oriData['couponCode'],
            $oriData['vourcher'] ? CouponExtEnum::COUPON_TYPE_MONEY : CouponExtEnum::COUPON_TYPE_PRODUCT,
            (int)bcmul($oriData['couponBuyPrice'], 100),
            (int)bcmul($oriData['singleValue'], 100),
            strtotime($oriData['dealBeginTime']),
            strtotime($oriData['couponEndTime']),
            CouponExtEnum::EXT_PLATFORM_MEITUAN,
            [],
            $oriData["thirdProductId"],
            $oriData['dealId'],
        );

        return $format;
    }

    /**
     * 核销券
     * @param string $couponCode
     * @param int $count
     * @param string $orderId
     * @return array
     * @throws GuzzleException
     */
    public function couponConsume($couponCode, int $count, string $orderId): array
    {
        // ****** 自己调试用 start ******
        if (in_array($couponCode, self::TEST_COUPON_CODE) && config('app.env') != 'prod') {

            $format      = self::returnFormatData(true, '', '', []);
            $couponCodes = [];
            for ($i = 0; $i < $count; $i++) {
                if ($count == 1) {
                    $couponCodes[] = ['couponCode' => $couponCode];
                } else {
                    $couponCodes[] = ['couponCode' => BaseService::createIncrNo(14)];
                }
            }

            $format['data'] = [
                'couponCodes' => $couponCodes,
                'couponSid'   => 1,
                'couponTitle' => '自己测试的牛逼券',
                'deduction'   => 10 * $count,
            ];
            return $format;
        }
        // ****** 自己调试用 end ******

        $params = [
            'couponCode' => $couponCode,
            'count'      => $count,
            'eOrderId'   => $orderId,
            'eId'        => $this->eId,
            'eName'      => $this->eName,
        ];

        $response = $this->curlPost('/tuangou/coupon/consume', $params);
        $format   = self::commonReturn($response);
//        $str = <<<STR
//{"status":true,"code":"OP_SUCCESS","msg":"","data":[],"oriData":{"couponCodes":["247476566299","311870566885"],"result":0,"rawTitle":"\u5ae9\u5ae9\u9e21\u8089\u4e32","dealId":866885566,"dealValue":15,"dealTitle":"\u5938\u7236\u70b8\u4e32\u5ae9\u5ae9\u9e21\u67f3\u4e32\uff0810\u4e32\uff091\u4efd","platformTitle":"3 \u4efd\u5ae9\u5ae9\u9e21\u67f3\u4e32\uff0810\u4e32\uff09","poiid":891713565,"message":""}}
//STR;
//
//        $format = json_decode($str, 1);

        if (!$format['status']) {
            return $format;
        }
        $oriData     = $format['oriData'];
        $couponCodes = [];
        foreach ($oriData['couponCodes'] as $oneCoupon) {
            $couponCodes[] = [
                'couponCode' => $oneCoupon
            ];
        }

        $format['data'] = [
            'couponCodes' => $couponCodes,
            'couponSid'   => $oriData['dealId'],
            'couponTitle' => $oriData['dealTitle'],
            'deduction'   => (int)bcmul($oriData['dealValue'], 100), // TODO 代金券，需要实际测试
        ];

        return $format;
    }

    /**
     * 撤销验券
     * @param string $couponCode
     * @return array
     * @throws GuzzleException
     */
    public function couponCancel(array $couponInfo): array
    {
        $couponCode = $couponInfo['couponExtCode'];
        $params     = [
            'couponCode' => $couponCode,
            'type'       => 1, // 固定
            'eId'        => $this->eId,
            'eName'      => $this->eName,
        ];

        // ****** 自己调试用 start ******
        if (in_array($couponCode, self::TEST_COUPON_CODE) && config('app.env') != 'prod') {
            return [
                'status' => true
            ];
        }
        // ****** 自己调试用 end ******

        $response = $this->curlPost('/tuangou/coupon/cancel', $params);
        return self::commonReturn($response);
    }

    /**
     * 已验券码查询
     * @param string $couponCode
     * @return array
     * @throws GuzzleException
     */
    public function couponQuery(string $couponCode): array
    {
        $params = [
            'couponCode' => $couponCode,
        ];

        $response = $this->curlPost('/tuangou/coupon/queryById', $params);
        $format   = self::commonReturn($response);
        if (!$format['status']) {
            return $format;
        }
        $oriData = $format['oriData'];

        $format['data'] = [
            'userPay'          => (int)bcmul($oriData['couponBuyPrice'], 100),
            'deduction'        => (int)bcmul($oriData['dealValue'], 100), // TODO 代金券，需要实际测试
            'cancelStatus'     => $oriData['couponCancelStatus'], // 0不可撤销 1可撤销
            'couponStatusDesc' => $oriData['couponStatusDesc'], // 使用状态文字
            'couponUseTime'    => strtotime($oriData['couponUseTime']), // 券使用时间
            'couponBeginTime'  => strtotime($oriData['dealBeginTime']), // 项目开始时间
            'couponSid'        => $oriData['dealId'],
            'couponTitle'      => $oriData['dealTitle'],
            'couponCode'       => $oriData['couponCode'],
            'couponType'       => $oriData['isVoucher'] ? CouponExtEnum::COUPON_TYPE_MONEY : CouponExtEnum::COUPON_TYPE_PRODUCT,
        ];

        return $format;
    }

    /**
     * 门店验券历史
     * @param string $date 2016-08-12
     * @param int $offset
     * @param int $limit
     * @return array
     * @throws GuzzleException
     */
    public function queryListByDate(string $date, int $offset = 0, int $limit = 20): array
    {
        $params = [
            'date'   => $date,
            'offset' => $offset,
            'limit'  => $limit,
        ];

        return $this->curlPost('/tuangou/coupon/queryListByDate', $params);
    }

//    public function reviewList(string $startDate, string $endDate, int $offset = 0, int $limit = 20)
//    {
//        $params = [
//            'startDate' => $startDate,
//            'endDate' => $endDate,
//            'offset' => $offset,
//            'limit' => $limit,
//        ];
//
//        return $this->curlPost('/review/reviewList', $params);
//
//    }

    /**
     * 查询团购订单结算明细
     * @param string $couponCode
     * @return array
     * @throws GuzzleException
     */
    public function queryTradeDetail(string $couponCode): array
    {
        $params = [
            'couponCode' => $couponCode,
        ];

        return $this->curlPost('/tuangou/coupon/queryTradeDetail', $params);
    }

    /**
     * 接口返回值统一处理
     * @param array $response
     * @return array
     */
    private function commonReturn(array $response): array
    {
        $code = $response['code'] ?? '';
        $data = $response['data'] ?? [];

        if (!$code || $code != 'OP_SUCCESS') {
            $msg = $response['msg'] ?? '';

            if (!is_array($data)) $data = [$data];

            return self::returnFormatData(false, $code, $msg, $data);
        }

        return self::returnFormatData(true, $code, '', $data);
    }

    /**
     * 生成美团的店铺号ePoiId
     * @param int $shopId
     * @return string
     */
    public static function encodeMTShopId(int $shopId): string
    {
        return EnvUtil::intProd(config('app.env')) . '-' . $shopId;
    }

    /**
     * 解析美团的ePoiId
     * @param string $ePoiId
     * @return array
     */
    public static function decodeMTShopId(string $ePoiId): array
    {
        $arr = explode('-', $ePoiId);

        return [
            $arr[0] ?? '', // appEnv 1/0
            (int)($arr[1] ?? 0), // shopId
        ];
    }
}
